//
//  main.swift
//  IDAPseudocodeOptimize
//
//  Created by wgd on 18/04/2019.
//  Copyright © 2019 wgd. All rights reserved.
//

import Foundation

struct RegexHelper {
    let regex: NSRegularExpression
    
    init(_ pattern: String) throws {
        try regex = NSRegularExpression(pattern: pattern,
                                        options: .caseInsensitive)
    }
    
    func hasMatch(_ input: String) -> Bool {
        let matches = regex.matches(in: input, options: [], range: NSMakeRange(0, input.count))
        return matches.count > 0
    }
    
    func matchOnlyOne(_ input: String) -> String? {
        let matches = regex.matches(in: input, options: [], range: NSMakeRange(0, input.count))
        if matches.count != 1 {
            return nil
        }
        return (input as NSString).substring(with: matches.first!.range)
    }
    
    func matchFirst(_ input: String) -> NSRange? {
        let matches = regex.matches(in: input, options: [], range: NSMakeRange(0, input.count))
        return matches.first?.range
    }
    
    func match(_ input:String) -> [NSRange] {
        return regex.matches(in: input, options: [], range: NSMakeRange(0, input.count))
            .map { $0.range }
    }
    
}

//extension String {
//    subscript(r: ClosedRange<Int>) -> String {
//        let start = index(startIndex, offsetBy: r.lowerBound)
//        let end = index(startIndex, offsetBy: r.upperBound)
//        return String(self[start...end])
//    }
//
//    subscript(r: Range<Int>) -> String {
//        get {
//            let start = index(startIndex, offsetBy: r.lowerBound)
//            let end = index(startIndex, offsetBy: r.upperBound)
//            return String(self[start..<end])
//        }
//    }
//}

extension String {
    func firstBlanks() -> Int {
        for (index, c) in self.enumerated() {
            if c != " " {
                return index
            }
        }
        return 0
    }
    
    func removeFirstblanks() -> String {
        return String(self.dropFirst(self.firstBlanks()))
    }
    
    func replaceVars(with values: [String:[String]], appendOrigina: Bool = false) -> String {
        var new = self
        do {
            let ranges = try RegexHelper("v[0-9]+").match(self)
                .sorted { $0.location > $1.location }
            for range in ranges {
                let key = NSString(string:new).substring(with: range)
                guard var value = values[key]?.last else { fatalError() }
                if appendOrigina {
                    value = "\(values[key]!.first!)(\(value))"
                }
                new = NSString(string: new).replacingCharacters(in: range, with: value)
            }
        } catch {
            print(error)
        }
        return new
    }
    
    func fixBR() -> String {
        let compares = ["(", ","]
        var strings: [String] = self.components(separatedBy: "\n").reversed()
        for (index, string) in strings.enumerated() {
            if compares.contains(String(string.last!)) && index > 0 {
                strings[index] = strings[index] + " " + strings[index-1].removeFirstblanks()
                strings[index-1] = ""
            }
        }
        var result = ""
        for string in strings.reversed() {
            result += string + "\n"
        }
        return result.dropLast().replacingOccurrences(of: "( ", with: "(")
        
    }
    
    func findRightParen(_ input: String) -> Int? {
        let right: Character = ")"
        let left: Character = "("
        guard let startIndex = self.range(of: input, range: self.startIndex..<self.endIndex)?.upperBound else { return nil}
        var count = 1
        for (index, c) in self.enumerated() {
            if index < startIndex.encodedOffset  {
                continue
            }
            if c == left {
                count += 1
            } else if c == right {
                count -= 1
            }
            if count == 0 {
                return index
            }
        }
        return nil
    }
    
    func fix_objc_msgSend() -> String {
        let msgSend = "_objc_msgSend"
        let msgSends = ["_objc_msgSend(", "(_objc_msgSend)("]
        let result = NSString(string:self)
        for msg in msgSends {
            guard let rightIndex = self.findRightParen(msg) else { continue }
            return result.replacingCharacters(in: NSRange(location: rightIndex, length: 1), with: "]")
                .replacingOccurrences(of: msg, with: "[")
        }
        if self.contains(msgSend) { fatalError() }
        return self
    }
    
    func filter_objc_retainAutoreleasedReturnValue() -> String {
        var result = self
        if self.contains("objc_retainAutoreleasedReturnValue(")  {
            result = result.replacingOccurrences(of: "objc_retainAutoreleasedReturnValue(", with: "")
            if result.components(separatedBy: ")").count != 2 {
                fatalError()
            }
            result = result.replacingOccurrences(of: ")", with: "")
        }
        return result
    }
}

// TODO: ----

testAll()

let args = CommandLine.arguments

let path = args[1]
let savePath = args[2]
let text = try String.init(contentsOfFile: path, encoding: .utf8)
var vars1 = try text.components(separatedBy: "\n\n")[0]
    .components(separatedBy: "\n")
    .dropFirst().dropFirst()
    .map { (string) -> String in
        do {
            if let result = try RegexHelper("v[0-9]+").matchOnlyOne(string) {
                return result
            }
        }
        do {
            
            if let result = try RegexHelper("\\s[^\\s]+;").matchOnlyOne(string) {
                var a = result.dropFirst().dropLast()
                while a.first == "*" {
                    a.removeFirst()
                }
                return String(a)
            }
        }
        print("error vars: \(string)")
        return "error: \(string)"
    }
//    .reduce(into: [:], { (result, key) in
//        result[key] = [key]
//    })
var varsDictionary: [String : [String]] = [:]
for var1 in vars1 {
    varsDictionary[var1] = [var1]
}

let news = text.components(separatedBy: "\n\n")[1].fixBR()
    .components(separatedBy: "\n")
    .map { $0.fix_objc_msgSend() }
    .map { $0.replacingOccurrences(of: "&OBJC_CLASS___", with: "")}
    .map { (line) -> String in
        if (line.contains("objc_release")) {
            let new = NSString(string:line).replacingCharacters(in: NSRange(location: line.firstBlanks(), length: 0), with: "// ")
            return new
        }
        
        let group = line.components(separatedBy: " = ")
        if group.count == 2 {
            let right = String(group[1].dropLast())
                .filter_objc_retainAutoreleasedReturnValue()
                .replaceVars(with: varsDictionary)
            let left = group.first!.removeFirstblanks()
            if (varsDictionary.keys.contains(left)) {
                let leading = String(repeating: " ", count: group[0].firstBlanks()) + "// "
                let oldLeft = varsDictionary[left]!.last!
                varsDictionary[left]?.append(right)
                let newRight = String(group[1].dropLast())
                    .filter_objc_retainAutoreleasedReturnValue()
                    .replaceVars(with: varsDictionary, appendOrigina:true)
                if oldLeft != left {
                    return leading + "\(varsDictionary[left]!.first!)_(\(oldLeft))" + " = " + newRight + ";"
                } else {
                    return leading + oldLeft + " = " + newRight + ";"
                }
            }
        } else if group.count > 2 {
            fatalError()
        }
        return line.replaceVars(with: varsDictionary, appendOrigina:true)
    }

var output = ""
output += text.components(separatedBy: "\n")[0] + "\n" + text.components(separatedBy: "\n")[1] + "\n"

for (index, new) in news.enumerated() {
    let matchs = ["{", "}"]
//    if index % 10 == 0 {
//        print("\(String.init(repeating: " ", count: new.firstBlanks()))// \(varsDictionary.count + 3 + index )")
//    }
//    print(new)
    if new == "" || new.contains("objc_release"){
        continue
    }
    var isSkip = false
    for match in matchs {
        if new.contains(match) {
            isSkip = true
            break
        }
    }
    if isSkip {
        output += new + "\n"
        continue
    }
    output += "\(new) // \(varsDictionary.count + 4 + index)" + "\n"
}

if !FileManager.default.fileExists(atPath: savePath) {
    FileManager.default.createFile(atPath: savePath, contents: nil, attributes: nil)
}
do {
    try output.write(toFile: savePath, atomically: false, encoding: .utf8)
} catch {
    print(error)
}



